#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>


#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>

#include <sys/stat.h>
#include <fcntl.h>

#define ENABLE_HTTP_RESPONSE 1



#define BUFFER_LENGTH 1024

typedef int (*RCALLBACK)(int fd);

// listenfd
int accept_cb(int fd);

// client
int recv_cb(int fd);
int send_cb(int fd);


struct conn_item
{
    int fd;

    char rbuffer[BUFFER_LENGTH];
    int rlen;
    char wbuffer[BUFFER_LENGTH];
    int wlen;

    union 
    {
        RCALLBACK accept_callback;
        RCALLBACK recv_callback;    
    }recv_t;
    RCALLBACK send_callback;
};

struct conn_item connlist[1024] = {0};

int epfd;

#if ENABLE_HTTP_RESPONSE

typedef struct conn_item connection_t;

int http_request(connection_t *conn) { 
    // Parse HTTP request
    return 0;
}

int http_response(connection_t *conn) {
#if 0
    conn->wlen = sprintf(conn->wbuffer, 
        "HTTP/1.1 200 OK\r\n"
        "Accept-Ranges: bytes\r\n"
        "Content-Length: 84\r\n"
        "Content-Type: text/html\r\n"
        "Date: Mon, 08 Sep 2025 06:30:52 GMT\r\n\r\n"
        "<html><head><title>demo.knight</title></head><body><h1>knight</h1></body></html>\r\n\r\n");
#else
    int filefd = open("/home/niko/workdir/code/cpp/0voice/reactor/index.html", O_RDONLY);
    if (filefd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    struct stat stat_buf;
    fstat(filefd, &stat_buf);

    conn->wlen = sprintf(conn->wbuffer, 
        "HTTP/1.1 200 OK\r\n"
        "Accept-Ranges: bytes\r\n"
        "Content-Length: %ld\r\n"
        "Content-Type: text/html\r\n"
        "Date: Mon, 08 Sep 2025 06:30:52 GMT\r\n\r\n", stat_buf.st_size);

    int count = read(filefd, conn->wbuffer + conn->wlen, BUFFER_LENGTH - conn->wlen);
    conn->wlen += count;

#endif
    return conn->wlen;
}
#endif

int set_event(int fd, int event, int flag)
{
    struct epoll_event ev;
    ev.events = event;
    ev.data.fd = fd;
    if (flag) {
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    } else {
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
    }
}

// listenfd
int accept_cb(int fd)
{
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    int clientfd = accept(fd, (struct sockaddr *)&clientaddr, &len);
    if (clientfd == -1) {
        perror("accept");
        return -1;
    }

    printf("clientfd: %d\n", clientfd);
    
    set_event(clientfd, EPOLLIN, 1);

    connlist[clientfd].fd = clientfd;
    memset(connlist[clientfd].rbuffer, 0, BUFFER_LENGTH);
    connlist[clientfd].rlen = 0;
    memset(connlist[clientfd].wbuffer, 0, BUFFER_LENGTH);
    connlist[clientfd].wlen = 0;
    connlist[clientfd].recv_t.recv_callback = recv_cb;
    connlist[clientfd].send_callback = send_cb;

    return clientfd;
}

// client
int recv_cb(int fd)
{
    char *buffer = connlist[fd].rbuffer;
    int ind = connlist[fd].rlen;
    int count = recv(fd, buffer + ind, BUFFER_LENGTH - ind, 0);
    if (0 == count) {
        printf("clientfd %d disconnected\n", fd);
        close(fd);
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        return -1;
    }
    connlist[fd].rlen += count;

#if 0
    memcpy(connlist[fd].wbuffer, connlist[fd].rbuffer, connlist[fd].rlen);
    connlist[fd].wlen = connlist[fd].rlen;

#else
    // http_request(&connlist[fd]);
    http_response(&connlist[fd]);

#endif

    set_event(fd, EPOLLOUT, 0);

    return count;
}

int send_cb(int fd)
{
    char *buffer = connlist[fd].wbuffer;
    int ind = connlist[fd].wlen;

    int count = send(fd, buffer, ind, 0);

    set_event(fd, EPOLLIN, 0);

    return count;
}

int main() {
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(struct sockaddr_in));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // serveraddr.sin_addr.s_addr = inet_addr("47.97.3.176");
    serveraddr.sin_port = htons(2048);

    if (-1 == bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) {
        perror("bind");
        close(socketfd);
        exit(EXIT_FAILURE);
    }

    if (-1 == listen(socketfd, 5)) {
        perror("listen");
        close(socketfd);
        exit(EXIT_FAILURE);
    }

    connlist[socketfd].fd = socketfd;
    connlist[socketfd].recv_t.accept_callback = accept_cb;


    epfd = epoll_create(1);

    struct epoll_event events[1024] = {0};
    
    set_event(socketfd, EPOLLIN, 1);

    while (1) {
        int nready = epoll_wait(epfd, events, 1024, -1);

        int i = 0;
        for (i = 0; i < nready; i++) {
            int connfd = events[i].data.fd; 

            if (events[i].events & EPOLLIN) {
                int count = connlist[connfd].recv_t.recv_callback(connfd);
                
                printf("recv count: %d <-- buffer: %s\n", count, connlist[connfd].rbuffer);
                
            }  else if (events[i].events & EPOLLOUT) {
            //    int count = send_cb(connfd);
                int count = connlist[connfd].send_callback(connfd); 
                printf("send --> buffer: %s\n", connlist[connfd].wbuffer);

            }
        }
    }
    


    // close(socketfd);
    return 0;

}
